using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using PowersourceUIToolkit;
using UCL.Powersource.Analysis;

namespace UCL.Powersource.TFS
{
    /// <summary>
    /// Tfs stat project collection configuration
    /// </summary>
    [DataContract(IsReference = true)]
    public class TfsTeamProjectCollectionConfiguration : PropertyChangedInitializedLifeCycle, ITfsTeamProjectCollectionConfiguration
    {
        [DataMember]
        public string IldasmPath { get; set; }
        [DataMember]
        public string LocalDropBasePath { get; set; }
        [DataMember]
        public string CodeIndexDBServer { get; set; }
        [DataMember]
        public string ExportPath { get; set; }
        [DataMember]
        public string WebServiceUrl { get; set; }
        [DataMember]
        public string DefaultProjectName { get; set; }

        public string CalculateDefaultProjectName()
        {
            var team =  this.FirstOrDefault(t => t.UniqueName.StartsWith("Default"));
            if(team != null) return team.UniqueName;
            if( TeamProjectConfigurations.Count > 0) return TeamProjectConfigurations[0].UniqueName;
            return "";
        }

        #region AllReportingContextCollection

        /// <summary>
        /// Field storage for property AllReportingContextCollection
        /// </summary>
        [DataMember]
        private ObservableCollection<ReportingContext> _AllReportingContextCollection = default(ObservableCollection<ReportingContext>);

        /// <summary>
        /// Property AllReportingContextCollection. TODODOC: Insert appropriate summary here
        /// </summary>
        public ObservableCollection<ReportingContext> AllReportingContextCollection
        {
            get { return _AllReportingContextCollection; }
        }

        #endregion

        #region CurrentReportingContext

        /// <summary>
        /// Field storage for property CurrentReportingContext
        /// </summary>
        private ReportingContext _CurrentReportingContext = default(ReportingContext);

        /// <summary>
        /// Property CurrentReportingContext. TODODOC: Insert appropriate summary here
        /// </summary>
        public ReportingContext CurrentReportingContext
        {
            get { return _CurrentReportingContext; }
            set { if (_CurrentReportingContext == value) return; _CurrentReportingContext = value; CurrentReportingContext_RecalculateDependencies(); }
        }

        private void CurrentReportingContext_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => CurrentReportingContext);
        }

        private void CurrentReportingContext_Recalculate()
        {
            if (AllReportingContextCollection == null)
            {
                _CurrentReportingContext = default(ReportingContext);
            }
            else
            {
                _CurrentReportingContext = AllReportingContextCollection.FirstOrDefault();
            }
            //Insert your code here

            CurrentReportingContext_RecalculateDependencies();
        }

        #endregion


        private TfsTeamProjectCollectionConfiguration()
        {
            InitializeDictionaries();
        }

        private void InitializeDictionaries()
        {
            if (_TeamProjectConfigurations == null) _TeamProjectConfigurations = new ObservableCollection<TfsTeamProjectConfiguration>();
            if (_AllReportingContextCollection == null) _AllReportingContextCollection = new ObservableCollection<ReportingContext>();
        }

        /// <summary>
        /// tfs stat project configuration
        /// </summary>
        /// <param name="uri"></param>
        public TfsTeamProjectCollectionConfiguration(string uri)
            : this()
        {
            this.Uri = uri;
            Initialize();
        }
        //public Guid TFSTeamCollection_Guid { get; set; }
        //public string Name { get; set; }
        //public string DomainName { get; set; }
        [DataMember]
        private ObservableCollection<TfsTeamProjectConfiguration> _TeamProjectConfigurations;

        /// <summary>
        /// Collection of the tfs stat project configurations of the stat project configurations
        /// </summary>
        public ObservableCollection<TfsTeamProjectConfiguration> TeamProjectConfigurations
        {
            get { return _TeamProjectConfigurations; }
        }

        [DataMember]
        private String _Uri;

        /// <summary>
        /// Project Uri
        /// </summary>
        public String Uri
        {
            get { return _Uri; }
            private set { _Uri = value; }
        }


        private ITfsTeamProjectCollection _TfsTeamProjectCollection;
        public ITfsTeamProjectCollection TfsTeamProjectCollection { get { return _TfsTeamProjectCollection; } set { _TfsTeamProjectCollection = value; } }

        [DataMember]
        private List<TimeFrame> _ProjectCollectionTimeFrames;
        public IEnumerable<ITimeFrame> ProjectCollectionTimeFrames { get { return _ProjectCollectionTimeFrames; } }


        private void Merge()
        {
            if (!IsInitialized) Initialize();
            if (TfsTeamProjectCollection == null) new NullReferenceException("You cannot merge with a null TFSTeamProjectCollection.");

            if (WebServiceUrl == null)
            {
                WebServiceUrl = "http://81.2.210.131:81/Powersource.WebApp.Test/Services/RemoteBuildBridge.svc";
            }

            MergeProjects();

            MergeTimeframes();

            //if (this.Uri != null && this.Uri.Contains("testprojectcollection"))
            //{
            //    var teams = this;
            //    var team1 = teams.First(a => a.UniqueName == "TestProjectTeam1");
            //    var team2 = teams.First(a => a.UniqueName == "TestProjectTeam2");
            //    team2.SolutionFilePath = "TestMvcApplication/TestMvcApplication.sln";
            //    var team3 = teams.First(a => a.UniqueName == "TestProjectTeam3");
            //    team3.SolutionFilePath = "TestWpfApplication/TestWpfApplication.sln";
            //    var team4 = teams.First(a => a.UniqueName == "TestProjectTeam4");
            //    team4.SolutionFilePath = "AnotherWpfApplication/AnotherWpfApplication.sln";
            //    var team5 = teams.First(a => a.UniqueName == "TestProjectTeam5");
            //}
            IoC.Get<IStatisticManager>().ClearStatisticsInvalidEntries(this);
            AllReportingContextCollection_Merge();
        }

        private void MergeProjects()
        {
            var wiStore = TfsTeamProjectCollection.GetService<WorkItemStore>();

            //Remove any projects removed in TFS
            for (int i = 0; i < TeamProjectConfigurations.Count; )
            {
                bool found = false;

                foreach (Project p in wiStore.Projects)
                {
                    if (p.Id == TeamProjectConfigurations[i].UniqueId)
                    {
                        found = true;
                        break;
                    }
                }

                if (found)
                {
                    i++;
                }
                else
                    TeamProjectConfigurations.RemoveAt(i);
            }

            //Sync with new projects added since last sync
            foreach (Project p in wiStore.Projects)
            {
                var tpc = TeamProjectConfigurations.SingleOrDefault(a => a.UniqueId == p.Id);
                if (tpc == null)
                {
                    tpc = new TfsTeamProjectConfiguration(p.Name, p.Id, this);
                    tpc.Uri = p.Uri.ToString();
                    TeamProjectConfigurations.Add(tpc);
                }
            }

            foreach (var proj in TeamProjectConfigurations)
            {
                proj.Synchronize();
            }

            if (string.IsNullOrWhiteSpace(DefaultProjectName)) DefaultProjectName = CalculateDefaultProjectName();
        }

        private void MergeTimeframes()
        {
            //ProjectCollectionTimeFrames sync

            //Create new ProjectCollectionTimeFrames if no such exists
            if (ProjectCollectionTimeFrames == null)
            {
                _ProjectCollectionTimeFrames = new List<TimeFrame>();
                //TfsTeamProjectCollection.pr
            }

            //Get the project NodeInfo array
            var project = TfsTeamProjectCollection.GetProject(DefaultProjectName);
            ICommonStructureService4 icss = TfsTeamProjectCollection.GetService<ICommonStructureService4>();
            if (project == null)
            {
                Log.Warn("Default project with name {0} was not found using first project at collection as the default one", DefaultProjectName);
                project = TfsTeamProjectCollection.GetProjectAtIndex(0);
            }
            var projInfo = icss.GetProjectFromName(project.Name);
            var prURI = projInfo.Uri;
            var lsStr = icss.ListStructures(prURI);
            var IterationNode = lsStr.FirstOrDefault(a => a.StructureType == "ProjectLifecycle" && a.Name == "Iteration");
            if (IterationNode != null)
            {
                var itXML = icss.GetNodesXml(new string[] { IterationNode.Uri }, true);
                var allChildren = itXML.SelectNodes("//Node[@StructureType='ProjectLifecycle']");

                //Remove any timeframes which are not present in tfs anymore
                RemoveOutdatedTimeFrames(icss, allChildren);

                ImportNewTimeFrames(icss, allChildren);
            }
        }

        private void RemoveOutdatedTimeFrames(ICommonStructureService4 icss, XmlNodeList allChildren)
        {
            for (int i = 0; i < _ProjectCollectionTimeFrames.Count; )
            {
                var found = false;

                foreach (XmlNode childNode in allChildren)
                {
                    try
                    {
                        var node = icss.GetNodeFromPath(childNode.Attributes["Path"].Value);
                        if (node == null) throw new NullReferenceException(string.Format("icss.GetNodeFromPath returned null for childnode {0}", childNode));
                        if (((ITimeFrameIteration)_ProjectCollectionTimeFrames[i]).NodePath == node.Path)
                        {
                            if (node.StartDate != null || node.FinishDate != null)
                                found = true;

                            break;
                        }
                    }
                    catch (Exception exc)
                    {
                        Log.Error(exc, "Ignoring childnode as IUnable get NodeInfo node for Xml node {0} (Path={1}; InnerXml={2}) ", childNode.Name, childNode.Attributes["Path"], childNode.InnerXml);
                    }
                }

                if (found) i++;
                else
                {
                    Log.Debug("Timeframe {0} no longer exists and has been removed.", ((ITimeFrameIteration)_ProjectCollectionTimeFrames[i]).IterationPath);
                    _ProjectCollectionTimeFrames.RemoveAt(i);
                }
            }
        }

        private void ImportNewTimeFrames(ICommonStructureService4 icss, XmlNodeList allChildren)
        {
            foreach (XmlNode childNode in allChildren)
            {
                try
                {
                    var node = icss.GetNodeFromPath(childNode.Attributes["Path"].Value);
                    //Foreach nodeinfo synchronize the information with ProjectCollectionTimeFrames by 
                    //either creating and attaching the new TimeframeIteration object and then merging
                    //or just merge the node with already existing TimeframeIterationObject
                    if (node == null) throw new NullReferenceException(string.Format("icss.GetNodeFromPath returned null for childnode {0}", childNode));
                    if (node.Name == "Iteration")
                    {
                        Log.Warn("Iteration node (Project Latest) skipped.");
                        continue;
                    }
                    if ((node.StartDate == null || node.FinishDate == null))
                    {
                        Log.Warn("Node {0} skipped as it contains no valid start/finish date", node.Path);
                        continue;
                    }
                    if (((DateTime)node.StartDate) > DateTime.Now)
                    {
                        Log.Warn("Node {0} skipped as its start date has not yet come.", node.Path);
                        continue;
                    }

                    var cTFI = (ITimeFrameIteration)_ProjectCollectionTimeFrames.FirstOrDefault(a => { var tfi = (a as ITimeFrameIteration); return (tfi != null && tfi.NodePath == node.Path); });
                    if (cTFI == null)
                    {

                        var localcTFI = new TimeFrameIteration();
                        _ProjectCollectionTimeFrames.Add(localcTFI);
                        cTFI = localcTFI;
                    }
                    cTFI.Merge(node);
                    Log.Debug("Timeframe {0} merged with node {1}.", cTFI.IterationPath, node.Uri);

                }
                catch (Exception exc)
                {
                    Log.Error(exc, "Ignoring childnode as IUnable get NodeInfo node for Xml node {0} (Path={1}; InnerXml={2}) ", childNode.Name, childNode.Attributes["Path"], childNode.InnerXml);
                }
            }
        }

        /// <summary>
        /// This function resets AllReportingContextCollection. It creates new one and inserts 1 default reportinitem 
        /// </summary>
        private void AllReportingContextCollection_Merge()
        {
            if (!IsInitialized) Initialize();
            //Synchornizing ReportingContext
            if (_AllReportingContextCollection == null)
                throw new InvalidOperationException("_AllReportingContextCollection should never be set to null. Internal error");
            else
            {


                List<ReportingContext> listofRCToDelete = new List<ReportingContext>();
                //Invalid reporting context will be marked to delete and remove afterwards
                //Valid reporting context will be merged
                foreach (var rc in _AllReportingContextCollection)
                {
                    if (rc.TfsTeamProjectCollectionConfiguration != this)
                    {
                        var oldTfsTPCC = rc.TfsTeamProjectCollectionConfiguration;
                        string oldTfsTPCCURI = "#NULL";
                        if (oldTfsTPCC != null) oldTfsTPCCURI = rc.TfsTeamProjectCollectionConfiguration.Uri;
                        Log.Error(null, "Ignored Reporitng context! There is a reporting context in {0} {1} which has different {0} assigned. Actual value is {2}", this.GetType().Name, this.Uri, oldTfsTPCCURI);
                        listofRCToDelete.Add(rc);
                    }
                    else
                    {
                        rc.Initialize();
                    }
                }
                //Remove them now
                foreach (var rcToDelete in listofRCToDelete)
                {
                    _AllReportingContextCollection.Remove(rcToDelete);
                }

                //if list of available AllReportingContextCollection is empty add new null one and merge

                if (_AllReportingContextCollection.Count == 0)
                {
                    // if there is no reporting context create default one
                    var rc = ReportingContext.CreateDefaults(this);
                    foreach (var it in rc)
                    {
                        _AllReportingContextCollection.Add(it);
                    }

                }
                CurrentReportingContext_Recalculate();
            }

        }

        public IEnumerator<ITfsTeamProjectConfiguration> GetEnumerator()
        {
            return TeamProjectConfigurations.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return TeamProjectConfigurations.GetEnumerator();
        }

        [IgnoreDataMember]
        private ILog Log = LogManager.GetLog(typeof(TfsTeamProjectCollectionConfiguration));

        protected override bool OnInitializing()
        {
            Log = LogManager.GetLog(typeof(TfsTeamProjectCollectionConfiguration));
            InitializeDictionaries();
            return (Log != null && _TeamProjectConfigurations != null && _AllReportingContextCollection != null);
        }

        protected override void OnDisposing()
        {
            if (_TeamProjectConfigurations != null) { _TeamProjectConfigurations.Clear(); _TeamProjectConfigurations = null; }
            if (_AllReportingContextCollection != null) { _AllReportingContextCollection.Clear(); _AllReportingContextCollection = null; };
            return;
        }

        /// <summary>
        /// Method ensures that changes made to either TFSTeamProjectConfiguration or TimeFrames are correctly populated to 
        /// selectable configurations
        /// </summary>
        public void Synchronize()
        {
            Merge();
        }

        public string Label
        {
            get { return Uri; }
        }

        public string Name { get; internal set; }



        public bool IsConnectedTo(ITfsTeamProjectCollection iTfsTeamProjectCollection)
        {
            return TfsTeamProjectCollection == iTfsTeamProjectCollection;
        }
    }
}
